﻿using System;
using Nofs.Net.Common.Interfaces.Domain;
using Nofs.Net.nofs.metadata.interfaces;
using biz.ritter.javapi.lang;
using biz.ritter.javapi.lang.reflect;
using biz.ritter.javapi.util;
using Nofs.Net.Common.Interfaces.Library;

namespace Nofs.Net.Domain.Impl
{
    public class FileObject : BaseDomainObject, IFileObject
    {

        private Object _object;
        private IAttributeAccessor _accessor;
        private IFileObjectStat _statInternal;
        private IFileObjectStat _realStat;
        private GenerationType _generation;
        private Method _method;
        private String _parentPath;
        private StatLazyLoader _statLoader;

        public FileObject(String parentPath, Object sender, IAttributeAccessor accessor, GenerationType generation)
            : this(parentPath, sender, null, accessor, generation)
        {
        }

        public FileObject(String parentPath, Object sender, Method method, IAttributeAccessor accessor, GenerationType generation)
        {
            _parentPath = parentPath;
            _object = sender;
            _accessor = accessor;
            _generation = generation;
            _method = method;
            _statLoader = null;
            _statInternal = null;
            _realStat = null;
        }

        public void SetStatLazyLoader(StatLazyLoader statLoader)
        {
            _statLoader = statLoader;
            _statInternal = null;
            _realStat = null;
        }

        private static bool RunMethod(Object sender, String methodName) ////throws Exception 
        {
            foreach (Method method in sender.getClass().getDeclaredMethods())
            {
                if (method.getName().compareTo(methodName) == 0 &&
                   method.getReturnType() == typeof(bool).getClass() &&
                   method.getParameterTypes().Length == 0)
                {
                    return (bool)method.invoke(sender, (Object[])null);
                }
            }
            throw new System.Exception("could not find method: " + methodName);
        }

        public String GetParentPath()
        {
            return _parentPath;
        }

        public bool CanMknod() //throws Exception 
        {
            return CanAdd() && (!InnerCollectionTypeIsFolderType(true) && InnerCollectionTypeIsFileType());
        }

        private bool CanAdd() //throws Exception 
        {
            if (_generation == GenerationType.DOMAIN_FOLDER)
            {
                IFolderObjectProperties fObj;
                bool addableObject = false;
                if (HasMethod())
                {
                    fObj = _accessor.GetFolderObject(GetMethod());
                    addableObject = typeof(List<object>).getClass().isAssignableFrom(GetMethod().getReturnType());
                }
                else
                {
                    fObj = _accessor.GetFolderObject(GetObject());
                    addableObject = typeof(List<object>).getClass().isAssignableFrom(GetObject().getClass());
                }
                if (addableObject && fObj.CanAdd())
                {
                    if (fObj.CanAddMethod().length() > 0)
                    {
                        return RunMethod(GetObject(), fObj.CanAddMethod());
                    }
                    return true;
                }
            }
            return false;
        }

        private bool InnerCollectionTypeIsFileType() //throws Exception
        {
            Class innerType;
            if (HasMethod())
            {
                innerType = _accessor.GetInnerCollectionType(GetMethod());
            }
            else
            {
                innerType = _accessor.GetInnerCollectionType(GetObject());
            }
            return _accessor.isDomainObject(innerType);
        }

        private bool InnerCollectionTypeIsFolderType(bool strict) //throws Exception 
        {
            Class innerType;
            if (HasMethod())
            {
                innerType = _accessor.GetInnerCollectionType(GetMethod());
            }
            else
            {
                innerType = _accessor.GetInnerCollectionType(GetObject());
            }
            return _accessor.isFolderObject(innerType, strict);
        }

        public bool CanMkdir() //throws Exception 
        {
            if (CanAdd() && GetGenerationType() == GenerationType.DOMAIN_FOLDER)
            {
                return InnerCollectionTypeIsFolderType(false);
            }
            return false;
        }

        private bool CanRemove() //throws Exception 
        {
            if (_generation == GenerationType.DOMAIN_FOLDER)
            {
                IFolderObjectProperties fObj;
                bool removableObject = false;
                if (HasMethod())
                {
                    fObj = _accessor.GetFolderObject(GetMethod());
                    removableObject = typeof(List<object>).getClass().isAssignableFrom(GetMethod().getReturnType());
                }
                else
                {
                    fObj = _accessor.GetFolderObject(GetObject());
                    removableObject = typeof(List<object>).getClass().isAssignableFrom(GetObject().getClass());
                }
                if (removableObject && fObj.CanRemove())
                {
                    if (fObj.CanRemoveMethod().length() > 0)
                    {
                        return RunMethod(GetObject(), fObj.CanRemoveMethod());
                    }
                    return true;
                }
                return removableObject && fObj.CanRemove();
            }
            return false;
        }

        public bool CanWrite() //throws Exception 
        {
            if (_generation == GenerationType.DATA_FILE)
            {
                IDomainObjectProperties dObj;
                if (HasMethod())
                {
                    dObj = _accessor.GetDomainObject(GetMethod());
                }
                else
                {
                    dObj = _accessor.GetDomainObject(GetObject());
                }
                return dObj.CanWrite();
            }
            return true;
        }

        public bool CanDeleteChildren() //throws Exception
        {
            return CanRemove() && !InnerCollectionTypeIsFolderType(false);
        }

        public bool CanRmdir() //throws Exception 
        {
            if (CanRemove() && GetGenerationType() == GenerationType.DOMAIN_FOLDER)
            {
                return InnerCollectionTypeIsFolderType(false);
            }
            return false;
        }

        public GenerationType GetGenerationType()
        {
            return _generation;
        }

        public String GetName() //throws Exception
        {
            return HasMethod() ? _accessor.getNameFromObject(GetMethod()) : _accessor.getNameFromObject(GetObject());
        }

        public bool CanSetName() //throws Exception 
        {
            return _accessor.canSetNameForObject(GetValue());
        }

        public void SetName(String value) //throws Exception
        {
            _accessor.setNameForObject(GetValue(), value);
        }

        public bool IsChildTypeCompatible(IFileObject possibleChild) //throws Exception 
        {
            Class myType = HasMethod()
                    ? _accessor.GetInnerCollectionType(GetMethod())
                    : _accessor.GetInnerCollectionType(GetObject());
            Class theirType = possibleChild.GetValue().getClass();
            return myType.isAssignableFrom(theirType) && theirType.isAssignableFrom(myType);
        }

        private IFileObjectStat GetStatInternal() //throws Exception 
        {
            if (_statLoader != null)
            {
                _statInternal = _statLoader.GetStat();
                _statLoader = null;
            }
            return _statInternal;
        }

        public IFileObjectStat GetStat() //throws Exception 
        {
            if (_realStat == null)
            {
                if (_accessor.HasAnyCustomMetadataManagement(GetObject()))
                {
                    _realStat = new CustomStat(GetStatInternal(), _accessor, GetObject());
                }
                else
                {
                    _realStat = GetStatInternal();
                }
            }
            return _realStat;
        }

        public Object GetObject()
        {
            return _object;
        }

        public Method GetMethod()
        {
            return _method;
        }

        public bool HasMethod()
        {
            return _method != null;
        }

        public Object GetValue() // throws IllegalArgumentException, IllegalAccessException, InvocationTargetException 
        {
            return HasMethod() ? GetMethod().invoke(GetObject(), (Object[])null) : GetObject();
        }

        public bool SupportsDirectIO() //throws Exception 
        {
            return (GetGenerationType() == GenerationType.DATA_FILE)
                    && (GetValue() is IProvidesUnstructuredData);
        }

        public IProvidesUnstructuredData GetReadWriteInterface() //throws Exception 
        {
            return (IProvidesUnstructuredData)GetValue();
        }
    }

}
